package com.cnbm.modules.messagequeue.consumer;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

/**
 * 1.并不用事先在mq上创建好 exchange 和  queue，只要 事先写好消费端（里面有exchange和queue的对应关系）然后启动就行了，如果不存在就会先创建
 * EAP 对应 exchange 划分： 一个模块一个 exchang，然后一个模块下有多台设备 那就一种类型设备一条队列，如果有多个相同类型设备那也是在一条队列中的
 * 如：
 *      exchangeA：
 *              DeviceAQueue
 *                  （routingkey：deviceA.1）
 *                  （routingkey：deviceA.2）
 *                  （routingkey：deviceA.3）
 *              DeviceBQueue
 *                  （routingkey：deviceB.1）
 *                  （routingkey：deviceB.2）
 *                   ......
 *              ..........
 *      exchangeB：
 *              DeviceAQueue
 *                  （routingkey：deviceA.1）
 *                  （routingkey：deviceA.2）
 *                  （routingkey：deviceA.3）
 *              DeviceBQueue
 *                  （routingkey：deviceB.1）
 *                  （routingkey：deviceB.2）
 *                   ......
 *              ..........
 * */
@Component
public class RabbitReceive {

    /**
     *  @Description  组合监听使用
     *  @Param
     *  @Return
     *  @Author caixiang
     *  @Date 2020/6/17  10:43
     **/
    // 1.这个注解是 监听某个队列 的；；2.如果交换机和 队列不存在那么 在启动的时候回去 创建它；
    //客户端 监听队列 ，这个队列并不需要事先创建，意思就是 我客户端就依据这个key 来接受数据
//    @RabbitListener(
//        bindings = @QueueBinding(
//                value = @Queue(value="queue1",durable = "true"),
//                exchange = @Exchange(name = "exchange1",durable = "true",type = "topic",ignoreDeclarationExceptions = "true"),
//                key = "springboot.*"
//        )
//    )
    //生成和配置 rabbitmq 的exchange 和 queue
    @RabbitListener(
            bindings = @QueueBinding(
                    value = @Queue(value="queuea",durable = "true"),
                    exchange = @Exchange(name = "exchange2",durable = "true",type = "topic",ignoreDeclarationExceptions = "true"),
                    key = "qgs.*"
            )
    )
    //只要有消息过来都会执行这个函数
    //注意：一条消息如果没有被签收，那么mq队列是不会把这条消息移除的，所以如果处理这条消息出现异常，那么需要回滚（已经录到数据库的数据要删除）不然对下次录入有影响。
    @RabbitHandler
    public void onMessage(Message<?> message, Channel channel)throws Exception{
        //1.收到消息后  业务端对message 进行处理
        System.out.println("===============================");
        System.out.println("端口:8004 消费体："+message.getPayload());
        System.out.println("端口:8004 消费头："+message.getHeaders().get("attr1")+"|"+message.getHeaders().get("attr2"));
        Thread.sleep(5000);
        //2.处理成功后 获取deliveryTag 并进行手工的ACK操作
        //因为我们在配置文件（yml）中 配置了手工签收消息，所以我们代码中处理完消息要签收（手工ack）
        //当生产者 生产出消息后 就会自动在message 的Header 上 赋上 DELIVERY_TAG
        Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);

        //multiple：为了减少网络流量，手动确认可以被批处理，当该参数为 true 时，则可以一次性确认 delivery_tag 小于等于传入值的所有消息
        //如果不去Ack 那么下次来 还会把 这个消息再消费一次。
        //参数1：deliveryTag:该消息的index； 参数2：multiple：是否批量.true:将一次性ack所有小于deliveryTag的消息。
        channel.basicAck(deliveryTag,false);
    }
}